package org.acooke.uykfd.db

import org.apache.empire.data.DataType
import org.apache.empire.db.DBColumn
import org.apache.empire.db.DBDatabase
import org.apache.empire.db.DBTableColumn


/**
 * The schema used to store information on tracks, related metadata,
 * and (possibly) graphs constructed from the two.
 */
object Schema extends DBDatabase {

  val MAX_VARCHAR = 333 // max for mysql with key

  abstract class Id[F](name: String, factory: F) extends ScalaTable[F](name, this, factory) {
    var ID = addColumn("ID", DataType.AUTOINC, 0, true, name + "_SEQ")
    setPrimaryKey(ID)
  }

  abstract class IdValue[F](name: String, factory: F) extends Id[F](name, factory) {
    var VALUE = addColumn("VALUE", DataType.TEXT, MAX_VARCHAR, true)
    addIndex(name + "_VALUE_IDX", true, Array(VALUE))
  }

  class Artists extends IdValue[ArtistTable]("ARTISTS", new ArtistFactory)
  val ARTISTS = new Artists

  class Canonicals extends IdValue[CanonicalTable]("CANONICALS", new CanonicalFactory)
  val CANONICALS = new Canonicals
  
  trait CanonicalRef {
    var CANONICAL: DBTableColumn
  }

  class ArtistCanonical 
    extends ScalaTable[ArtistCanonicalTable]("ARTIST_CANONICAL", this, new ArtistCanonicalFactory)
    with CanonicalRef {

    var CANONICAL = addColumn("CANONICAL", DataType.INTEGER, 0, true)
    addRelation(CANONICAL.referenceOn(CANONICALS.ID))
    var ARTIST = addColumn("ARTIST", DataType.INTEGER, 0, true)
    addRelation(ARTIST.referenceOn(ARTISTS.ID))
  }
  val ARTIST_CANONICAL = new ArtistCanonical

  abstract class IdValueCanonical[F](name: String, factory: F)
    extends IdValue[F](name, factory)
    with CanonicalRef {

    var CANONICAL = addColumn("CANONICAL", DataType.INTEGER, 0, true)
    addRelation(CANONICAL.referenceOn(CANONICALS.ID))
  }

  class Albums extends IdValueCanonical[AlbumTable]("ALBUMS", new AlbumFactory)
  val ALBUMS = new Albums

  class SongTitles extends IdValueCanonical[SongTitleTable]("SONG_TITLES", new SongTitleFactory)
  val SONG_TITLES = new SongTitles

  class Tracks extends Id[TrackTable]("TRACKS", new TrackFactory) {
    var ARTIST = addColumn("ARTIST", DataType.INTEGER, 0, true)
    var ALBUM = addColumn("ALBUM", DataType.INTEGER, 0, true)
    var SONG_TITLE = addColumn("SONG_TITLE", DataType.INTEGER, 0, true)
    var TRACK_NUMBER = addColumn("TRACK_NUMBER", DataType.INTEGER, 0, true)
    var PATH = addColumn("PATH", DataType.TEXT, MAX_VARCHAR, true)
    addRelation(ARTIST.referenceOn(ARTISTS.ID))
    addRelation(ALBUM.referenceOn(ALBUMS.ID))
    addRelation(SONG_TITLE.referenceOn(SONG_TITLES.ID))
    addIndex("TRACKS_ALL_IDX", true, Array[DBColumn](ARTIST, ALBUM, SONG_TITLE, TRACK_NUMBER))
  }
  val TRACKS = new Tracks

  class Properties extends ScalaTable[PropertyTable]("PROPERTIES", this, new PropertyFactory) {
    var NAME = addColumn("NAME", DataType.TEXT, MAX_VARCHAR, true)
    setPrimaryKey(NAME)
    var VALUE = addColumn("VALUE", DataType.TEXT, MAX_VARCHAR, false)
  }
  val PROPERTIES = new Properties

  abstract class Edges[F, O <: Schema.Id[_]](name: String, over: O, factory: F)
    extends ScalaTable[F](name, this, factory) {

    var SOURCE = addColumn("SOURCE", DataType.INTEGER, 0, true)
    addRelation(SOURCE.referenceOn(over.ID))
    var SINK = addColumn("SINK", DataType.INTEGER, 0, true)
    addRelation(SINK.referenceOn(over.ID))
    setPrimaryKey(SOURCE, SINK)
  }

  abstract class WeightedEdges[F, O <: Schema.Id[_]](name: String, over: O, factory: F)
    extends Edges[F,O](name, over, factory) {

    var WEIGHT = addColumn("WEIGHT", DataType.INTEGER, 0, true)
  }

  class LfmArtistTags
    extends WeightedEdges[LfmArtistTagTable, Canonicals](
      "LFM_ARTIST_TAGS", CANONICALS, new LfmArtistTagFactory)
  val LFM_ARTIST_TAGS = new LfmArtistTags

  class LfmArtistConnections
    extends WeightedEdges[LfmArtistConnectionTable, Artists](
      "LFM_ARTIST_CONNECTIONS", ARTISTS, new LfmArtistConnectionFactory)
  val LFM_ARTIST_CONNECTIONS = new LfmArtistConnections

  class LfmArtistBacklinks
    extends Edges[LfmArtistBacklinkTable, Artists](
      "LFM_ARTIST_BACKLINKS", ARTISTS, new LfmArtistBacklinkFactory)
  val LFM_ARTIST_BACKLINKS = new LfmArtistBacklinks

  class LfmArtistDistances
    extends WeightedEdges[LfmArtistDistanceTable, Artists](
      "LFM_ARTIST_DISTANCES", ARTISTS, new LfmArtistDistanceFactory)
  val LFM_ARTIST_DISTANCES = new LfmArtistDistances

  val orderedTables = 
    List[ScalaTable[_]](CANONICALS, 
                        ARTISTS, ARTIST_CANONICAL, ALBUMS, SONG_TITLES, TRACKS,
                        PROPERTIES,
                        LFM_ARTIST_TAGS, LFM_ARTIST_CONNECTIONS, LFM_ARTIST_BACKLINKS, LFM_ARTIST_DISTANCES)
 
}
